package cn.yq.aio.utils;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothSocket;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;

import androidx.appcompat.app.AlertDialog;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Set;
import java.util.UUID;

/**
 * @AUTHOR YangLu
 * @Time 2021-03-26
 * @todo 部分设备需要在权限管理内，将定位权限取消再允许即可搜索到蓝牙设备
 */
public class BleUtils {

    public static final String TAG = "BleUtils";

    //蓝牙的扫描结果(经典蓝牙)
    private BroadcastReceiver discoveryReceiver;
    BluetoothAdapter adapter;
    BluetoothDevice targetDevice;
    BluetoothGatt bluetoothGatt;
    ScanCallback scanCallback;
    Activity context;
    boolean support = true;
    boolean curTypeIsBle = false;
    CallBack callBack;
    ClassicsCallBack classicsCallBack;

    BluetoothSocket bluetoothSocket;
    boolean classicsRead = true;


    public BleUtils(Activity context, CallBack callBack) {
        this.context = context;
        this.callBack = callBack;
        adapter = BluetoothAdapter.getDefaultAdapter();
        support = adapter != null;
        //蓝牙开关的监听
        IntentFilter filter = new IntentFilter();
        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
        context.registerReceiver(stateReceiver, filter);
    }

    public BleUtils(Activity context, ClassicsCallBack callBack) {
        this.context = context;
        this.classicsCallBack = callBack;
        adapter = BluetoothAdapter.getDefaultAdapter();
        support = adapter != null;
        //蓝牙开关的监听
        IntentFilter filter = new IntentFilter();
        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
        context.registerReceiver(stateReceiver, filter);
    }

    //启动蓝牙
    public void open(boolean curTypeIsBle) {
        this.curTypeIsBle = curTypeIsBle;
        if (!checkSupport()) return;
        if (adapter.isEnabled()) {
            start();
            return;
        }
        new AlertDialog.Builder(context)
                .setTitle("提示")
                .setMessage("请开启蓝牙继续使用")
                .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                })
                .setPositiveButton("开启", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                        Intent enableBtIntent =
                                new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                        context.startActivity(enableBtIntent);
                    }
                }).create().show();
    }

    //开始扫描蓝牙设备
    private void start() {
        //查询已配对设备
        Set<BluetoothDevice> pairedDevices = adapter.getBondedDevices();
        if (pairedDevices.size() > 0) {
            for (BluetoothDevice device : pairedDevices) {
                if (curTypeIsBle ? callBack.isNeedDevice(device.getName()) :
                        classicsCallBack.isNeedDevice(device.getName())) {
                    targetDevice = device;
                    Log.e(TAG, "查询到设备：" + device.getName());
                    break;
                }
            }
        }

        if (targetDevice != null) {
            if (curTypeIsBle) {
                bleConnect();
            } else {
                //开始连接
                classicsConnect();
            }
            return;
        }

        if (curTypeIsBle) {
            //低功耗的扫描
            bleScan();
        } else {
            //发现设备,在discoveryReceiver 中回调 (此处为经典蓝牙扫描)
            classicsScan();
        }
    }

    //经典蓝牙扫描----------------------------------------------------------------------------
    private void classicsScan() {
        discoveryReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
                // 当 Discovery 发现了一个设备
                Log.e(TAG, "discoveryReceiver : action = " + action);
                if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                    // 从 Intent 中获取发现的 BluetoothDevice
                    BluetoothDevice device =
                            intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                    Log.e(TAG, "扫描到设备：" + device.getName() + " == " + device.getAddress());
                    if (classicsCallBack.isNeedDevice(device.getName())) {
                        targetDevice = device;
                        //找到蓝牙设备了，停止扫描
                        adapter.cancelDiscovery();
                        //是否绑定 : 回调 BluetoothDevice.ACTION_BOND_STATE_CHANGED
                        if (targetDevice.getBondState() == BluetoothDevice.BOND_NONE) {
                            boolean bond = targetDevice.createBond();
//                            targetDevice.setPin("000002".getBytes());
                            Log.e(TAG, "bond : " + bond);
                        } else {
                            //开始连接
                            classicsConnect();
                        }
                    }
                } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {//配对成功了
                    if (targetDevice.getBondState() == BluetoothDevice.BOND_BONDED) {
                        //开始连接
                        Log.e(TAG, "开始连接");
                        classicsConnect();
                    }
                    Log.e(TAG, "ACTION_BOND_STATE_CHANGED：" + targetDevice.getBondState());
                }
            }
        };
        //发现蓝牙设备的监听
        IntentFilter filter1 = new IntentFilter(BluetoothDevice.ACTION_FOUND);
        filter1.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
        context.registerReceiver(discoveryReceiver, filter1);

        Log.e(TAG, "经典扫描开始...");
        boolean result = adapter.startDiscovery();
        String msg = result ? "开始扫描蓝牙" : "扫描蓝牙失败";
        Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
        Log.e(TAG, "经典扫描开始结果..." + result);
    }
    //经典蓝牙扫描----------------------------------------------------------------------------

    //经典蓝牙连接------------------------------------------------
    private void classicsConnect() {
        if (!classicsCallBack.needConnect(targetDevice)) {
            Log.e(TAG, "不需要自动连接");
            return;
        }
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    bluetoothSocket =
                            targetDevice.createRfcommSocketToServiceRecord(
                                    UUID.fromString(classicsCallBack.getUUid()));
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        while (classicsRead) {
                            try {
                                bluetoothSocket.connect();
                                classicsRead = true;

                                byte[] buffer = new byte[1024];  // buffer store for the stream
                                int bytes; // bytes returned from read()
                                InputStream inputStream = bluetoothSocket.getInputStream();
                                while (classicsRead) {
                                    try {
                                        // Read from the InputStream
                                        inputStream.read(buffer);
                                        // Send the obtained bytes to the UI activity
                                        byteToHex(buffer);
                                    } catch (IOException e) {
                                        break;
                                    }
                                }
                            } catch (IOException e) {
                                Log.e(TAG, "classicsConnect connect fail : " + e.getMessage());
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }).start();
            }
        }).start();
    }
    //经典蓝牙连接------------------------------------------------


    //此处为ble低功耗蓝牙的开始扫描start
    // ----------------------------------------------------------------------------
    private void bleScan() {
        scanCallback = new ScanCallback() {
            @Override
            public void onScanResult(int callbackType, ScanResult result) {
                super.onScanResult(callbackType, result);
                Log.e(TAG,
                        "onScanResult : callbackType = " + callbackType + " == " + result.getDevice().getName());
                if (!TextUtils.isEmpty(result.getDevice().getName()) &&
                        callBack.isNeedDevice(result.getDevice().getName())) {
                    targetDevice = result.getDevice();
                    adapter.getBluetoothLeScanner().stopScan(scanCallback);
                    Log.e(TAG, "停止扫描，已找到设备：" + targetDevice.getName());
                    if (callBack.needConnect(targetDevice)) {
                        //连接这个蓝牙设备
                        bleConnect();
                    }
                }
            }

            @Override
            public void onBatchScanResults(List<ScanResult> results) {
                super.onBatchScanResults(results);
                Log.e(TAG, "onBatchScanResults : " + results.size());
            }

            @Override
            public void onScanFailed(int errorCode) {
                super.onScanFailed(errorCode);
                Log.e(TAG, "onScanFailed : " + errorCode);
                Toast.makeText(context, "扫描蓝牙失败：" + errorCode, Toast.LENGTH_SHORT).show();
            }
        };
        Log.e(TAG, "ble 开始扫描...");
        adapter.getBluetoothLeScanner().startScan(scanCallback);
    }

    //ble低功耗蓝牙的连接
    private void bleConnect() {
        BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {

            @Override
            public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
                super.onConnectionStateChange(gatt, status, newState);
                /**
                 * 连接状态：
                 *    * The profile is in disconnected state   *public static final int
                 *    STATE_DISCONNECTED  = 0;
                 *    * The profile is in connecting state     *public static final int
                 *    STATE_CONNECTING    = 1;
                 *    * The profile is in connected state      *public static final int
                 *    STATE_CONNECTED    = 2;
                 *    * The profile is in disconnecting state  *public static final int
                 *    STATE_DISCONNECTING = 3;
                 *
                 */
                if (BluetoothGatt.STATE_CONNECTED == newState) {
                    Log.e(TAG, "onConnectionStateChange : 连接成功");
                    //必须有，可以让onServicesDiscovered显示所有Services
                    gatt.discoverServices();
                } else if (BluetoothGatt.STATE_DISCONNECTED == newState) {
                    Log.e(TAG, "onConnectionStateChange : 断开连接:");
                    callBack.breakConnect();
                }
            }

            @Override
            public void onServicesDiscovered(BluetoothGatt gatt, int status) {
                super.onServicesDiscovered(gatt, status);
                //发现相关服务
                Log.e(TAG, "onServicesDiscovered : " + status);
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    List<BluetoothGattService> list = gatt.getServices();
                    for (BluetoothGattService bluetoothGattService : list) {
                        String str = bluetoothGattService.getUuid().toString();
                        Log.e(TAG, "GattServiceUuid ：" + str);
                        if (!callBack.isNeedService(bluetoothGattService.getUuid().toString()))
                            continue;
                        List<BluetoothGattCharacteristic> gattCharacteristics = bluetoothGattService
                                .getCharacteristics();
                        for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
                            Log.e(TAG, "gattCharacteristicUuid ：" + gattCharacteristic.getUuid());
                            if (!callBack.isNeedCharacteristic(gattCharacteristic.getUuid().toString()))
                                continue;
                            callBack.findCharacteristic(gattCharacteristic);

                            int charaProp = gattCharacteristic.getProperties();
                            if ((charaProp & BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
                                Log.e(TAG, "read_chara=");
                            }
                            if ((charaProp & BluetoothGattCharacteristic.PROPERTY_WRITE) > 0) {
                                Log.e(TAG, "write_chara=");
                            }
                            if ((charaProp & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) > 0) {
                                Log.e(TAG, "write_chara=");
                            }
                            if ((charaProp & BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
                                Log.e(TAG, "notify_chara=");
                            }
                            if ((charaProp & BluetoothGattCharacteristic.PROPERTY_INDICATE) > 0) {
                                Log.e(TAG, "indicate_chara=");
                            }
                        }
                    }
                }
            }

            @Override
            public void onCharacteristicRead(BluetoothGatt gatt,
                                             BluetoothGattCharacteristic gattCharacteristic,
                                             int status) {
                super.onCharacteristicRead(gatt, gattCharacteristic, status);
                Log.e(TAG,
                        "1.onCharacteristicRead : " + byteToHex(gattCharacteristic.getValue()));
            }

            @Override
            public void onCharacteristicWrite(BluetoothGatt gatt,
                                              BluetoothGattCharacteristic gattCharacteristic,
                                              int status) {
                super.onCharacteristicWrite(gatt, gattCharacteristic, status);
                Log.e(TAG, "onCharacteristicWrite : " + byteToHex(gattCharacteristic.getValue()));
            }

            @Override
            public void onCharacteristicChanged(BluetoothGatt gatt,
                                                BluetoothGattCharacteristic gattCharacteristic) {
                super.onCharacteristicChanged(gatt, gattCharacteristic);
                Log.e(TAG, "onCharacteristicChanged : " + byteToHex(gattCharacteristic.getValue()));
                callBack.onReceiveValue(gattCharacteristic.getValue());
            }

            @Override
            public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
                                         int status) {
                super.onDescriptorRead(gatt, descriptor, status);
                Log.e(TAG, "onDescriptorRead : ");
            }

            @Override
            public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
                                          int status) {
                super.onDescriptorWrite(gatt, descriptor, status);
                Log.e(TAG, "onDescriptorWrite");
            }

            @Override
            public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
                super.onReliableWriteCompleted(gatt, status);
                Log.e(TAG, "onReliableWriteCompleted");
            }

            @Override
            public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
                super.onReadRemoteRssi(gatt, rssi, status);
                Log.e(TAG, "onReadRemoteRssi");
            }

            @Override
            public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
                super.onMtuChanged(gatt, mtu, status);
                Log.e(TAG, "onMtuChanged");
            }
        };

        Log.e(TAG, "ble 开始连接...");
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            bluetoothGatt = targetDevice.connectGatt(context,
                    false, bluetoothGattCallback, BluetoothDevice.TRANSPORT_LE);
        } else {
            bluetoothGatt = targetDevice.connectGatt(context, false, bluetoothGattCallback);
        }
    }

    //发送内容,会触发BluetoothGattCallback#onCharacteristicWrite()回调
    public void send(BluetoothGattCharacteristic gattCharacteristic) {
        boolean result = bluetoothGatt.writeCharacteristic(gattCharacteristic);
        Log.e(TAG, "send : " + result);
    }

    //读取内容,会触发BluetoothGattCallback#onCharacteristicRead()
    public void read(BluetoothGattCharacteristic gattCharacteristic) {
        boolean result = bluetoothGatt.readCharacteristic(gattCharacteristic);
        Log.e(TAG, "read : " + result);
    }

    public void notify(BluetoothGattCharacteristic gattCharacteristic) {
        bluetoothGatt.readCharacteristic(gattCharacteristic);
        //设置订阅notificationGattCharacteristic值改变的通知
        boolean result = bluetoothGatt.setCharacteristicNotification(
                gattCharacteristic, true);
        Log.e(TAG, "setCharacteristicNotification : " + result);
        //获取其对应的通知Descriptor
        BluetoothGattDescriptor descriptor =
                gattCharacteristic.getDescriptor(gattCharacteristic.getUuid());
        if (descriptor != null) {
            //设置通知值
            descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
            boolean descriptorResult = bluetoothGatt.writeDescriptor(descriptor);
            Log.e(TAG, "notify ：" + descriptorResult);
        } else {
            Log.e(TAG, "notify " + gattCharacteristic.getUuid() + " fail");
        }
    }

    //此处为ble低功耗蓝牙的end----------------------------------------------------------------------------

    //蓝牙的链接状态
    private BroadcastReceiver stateReceiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {
            Log.e(TAG, "onReceive---------" + intent.getAction());
            switch (intent.getAction()) {
                case BluetoothAdapter.ACTION_STATE_CHANGED:
                    int blueState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0);
                    switch (blueState) {
                        case BluetoothAdapter.STATE_TURNING_ON:
                            Log.e(TAG, "onReceive---------STATE_TURNING_ON");
                            start();
                            break;
                        case BluetoothAdapter.STATE_ON:
                            Log.e(TAG, "onReceive---------STATE_ON");
                            break;
                        case BluetoothAdapter.STATE_TURNING_OFF:
                            Log.e(TAG, "onReceive---------STATE_TURNING_OFF");
                            break;
                        case BluetoothAdapter.STATE_OFF:
                            Log.e(TAG, "onReceive---------STATE_OFF");
                            break;
                    }
                    break;
            }
        }
    };

    private boolean checkSupport() {
        if (!support) {
            // 说明此设备不支持蓝牙操作
            Toast.makeText(context, "该设备不支持蓝牙", Toast.LENGTH_SHORT).show();
        }
        return support;
    }

    public void close() {
        classicsRead = false;
        context.unregisterReceiver(stateReceiver);
        if (discoveryReceiver != null) {
            context.unregisterReceiver(discoveryReceiver);
        }
        if (scanCallback != null) {
            adapter.getBluetoothLeScanner().stopScan(scanCallback);
        }
        if (bluetoothGatt != null) {
            bluetoothGatt.disconnect();
            bluetoothGatt.close();
        }
        if (bluetoothSocket != null) {
            try {
                bluetoothSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (adapter != null) {
            adapter.cancelDiscovery();
            adapter = null;
        }
    }

    public interface CallBack {
        //是否是指定设备
        boolean isNeedDevice(String bleDeviceName);

        //是否直接连接,false的话  后续获取服务、特征等不会执行
        boolean needConnect(BluetoothDevice targetDevice);

        //是否是需要的服务
        boolean isNeedService(String uuid);

        //是否是需要的特征
        boolean isNeedCharacteristic(String uuid);

        //需要的特征
        void findCharacteristic(BluetoothGattCharacteristic gattCharacteristic);

        //收到通知的数据
        void onReceiveValue(byte[] data);

        //断开连接
        void breakConnect();
    }

    public interface ClassicsCallBack {
        //是否是指定设备
        boolean isNeedDevice(String bleDeviceName);

        //是否直接连接
        boolean needConnect(BluetoothDevice targetDevice);

        //获取uuid
        String getUUid();
    }

    public static String byteToHex(byte[] bytes) {
        String strHex = "";
        StringBuilder sb = new StringBuilder("");
        for (int n = 0; n < bytes.length; n++) {
            int i = bytes[n] & 0xFF;
            strHex = Integer.toHexString(i);
            sb.append((strHex.length() == 1) ? "0" + strHex : strHex); // 每个字节由两个字符表示，位数不够，高位补0
            sb.append(" ");
        }
        return sb.toString().trim();
    }
}
